﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace System
{
    public static class TypeExtensions
    {
        public static IEnumerable<PropertyInfo> GetProperties(this Type self)
        {
#if WINRT
            return self.GetTypeInfo().DeclaredProperties;
#endif
        }
        public static PropertyInfo GetProperty(this Type self, string name)
        {
#if WINRT
            var properties = self.GetProperties();
            return properties.FirstOrDefault((pi) =>
                {
                    return pi.Name == name;
                });
#endif
        }

        public static MethodInfo GetMethod(this Type self, string name)
        {
#if WINRT
            return self.GetTypeInfo().GetDeclaredMethod(name);
#else
            return self.GetMethod(name);
#endif
        }

        public static IEnumerable<MethodInfo> GetMethods(this Type self, string name)
        {
            return self.GetTypeInfo().DeclaredMethods;
        }


        public static bool IsValueType(this Type self)
        {
#if WINRT
            return self.GetTypeInfo().IsValueType;
#endif
        }

#if WINRT
        public static IEnumerable<Type> GetInterfaces(this Type self)
        {
            return self.GetTypeInfo().ImplementedInterfaces;
        }
        public static bool IsSubclassOf(this Type self, Type another)
        {
            var typeInfo1 = self.GetTypeInfo();
            var typeInfo2 = another.GetTypeInfo();
            var result = typeInfo2.IsAssignableFrom(typeInfo1);
            return result;
        }
#endif

        public static bool IsAssignableFrom(this Type type, Type parentType)
        {
            return type.GetTypeInfo().IsAssignableFrom(parentType.GetTypeInfo());
        }


        public static bool IsEnum(this Type type)
        {
            return type.GetTypeInfo().IsEnum;
        }


        public static bool IsPrimitive(this Type type)
        {
            return type.GetTypeInfo().IsPrimitive;
        }


        public static Type GetBaseType(this Type type)
        {
            return type.GetTypeInfo().BaseType;
        }


        public static bool IsGenericType(this Type type)
        {
            return type.GetTypeInfo().IsGenericType;
        }


        public static Type[] GetGenericArguments(this Type type)
        {
            return type.GetTypeInfo().GenericTypeArguments;
        }


        public static object GetPropertyValue(this Object instance, string propertyValue)
        {
            return instance.GetType().GetTypeInfo().GetDeclaredProperty(propertyValue).GetValue(instance);
        }

        public static T GetCustomAttribute<T>(this Type self, bool inherit) where T : Attribute
        {
            return self.GetTypeInfo().GetCustomAttribute(typeof(T), inherit) as T;
        }

        //public static TypeInfo GetTypeInfo(this Type type)
        //{
        //    IReflectableType reflectableType = (IReflectableType)type;
        //    return reflectableType.GetTypeInfo();
        //}

        public static Type GetClosedGenericType(this object obj, Type openGenericType)
        {
            // If the object is null then just return null

            if (obj == null)
                return null;

            // Otherwise use reflection to get all interfaces implemented by the type

            IEnumerable<Type> implementedInterfaces = obj.GetType().GetTypeInfo().ImplementedInterfaces;

            // Return the first interface matching the specified open generic type (or null if this interface is not implemented)

            return implementedInterfaces.Where(i => i.IsConstructedGenericType && i.GetGenericTypeDefinition() == openGenericType)
                                        .FirstOrDefault();
        }

    }

}
